package pt.inescid.components.overlaymanager;
import rice.p2p.commonapi.Id;
import rice.p2p.commonapi.NodeHandle;

import java.util.List;
import java.util.Vector;
import java.util.Arrays;

/**
 * 
 * NodeMetrics
 * 
 * @author Filipe Paredes
 * @author Sergio Esteves (sesteves at gsd.inesc-id.pt)
 */
public class NodeMetrics {
  
  // nodeHandle
  private NodeHandle node;
  
  // Metrics for this node
  private double prox, cpu = 0, mem = 0, bw = 0;
  private long storage = 0;

  // application identifiers
  private List<String> appIDs = null;

  // node's minimum ratio
  private double minRatio = 0;
  
  // Current rate defining the availability of the corresponding node
  private double rate;
  
  // Sequence Number of the last update message that change this metrics
  private long seqNumber;
  
  private boolean isAvailable;
  
  public NodeMetrics(NodeHandle node, double prox) {
	  this.node = node;
	  this.prox = prox;
  }
  
  public NodeMetrics(NodeHandle node, double prox, double cpu, double mem, double bw,
		  String[] appIDs, double minRatio) {
    this(node, prox);
    this.cpu = cpu;
    this.mem = mem;
    this.bw = bw;
    this.appIDs = appIDs == null? null : Arrays.asList(appIDs);
    this.minRatio = minRatio;
    this.seqNumber = 0;
    this.updateRate();
  }
  
  public Id getId(){
      return node.getId();
  }
  
  public NodeHandle getNodeHandle(){
    return node;
  }
  
  public double getProximity(){
      return this.prox;
  }
  
  public double getRate(){
      return this.rate;
  }
  
  public long getSeqNumber() {
    return seqNumber;
  }
  
  public Vector<Double> getParams(){
      Vector<Double> params = new Vector<Double>();
      params.add(0,cpu);
      params.add(1,mem);
      params.add(2,bw);
      params.add(3,prox);
      
      return params;
  }
  
  public void updateRate(){
    // Measured sum of the parts, favors resources (60%) 
    rate = ((10000 / prox) * 0.4) + (cpu * 0.2) + (mem * 0.2) + (bw * 0.2);
    
    //rate = 1;
    //rate = 10000 / prox;
    //rate = (cpu * 0.33) + (mem * 0.33) + (bw * 0.33);
    //rate = ((10000 / prox) * 0.5) + (cpu * 0.166) + (mem * 0.166) + (bw * 0.166);
    //rate = ((10000 / prox) * 0.75) + (cpu * 0.083) + (mem * 0.083) + (bw * 0.083);
    //rate = ((10000 / prox) * 0.25) + (cpu * 0.25) + (mem * 0.25) + (bw * 0.25);
  }
  
  public void updateMetrics(double cpu, double mem, double bw, 
		  double storage, String[] appIDs, boolean consume) {

	  if (consume) {
		  cpu -= cpu;
		  mem -= mem;
		  bw -= bw;
		  storage -= storage;           
	  } else {
		  cpu += cpu;
		  mem += mem;
		  bw += bw;
		  storage += storage;
	  }
	  if(appIDs != null) this.appIDs = Arrays.asList(appIDs);
	  this.updateRate();
  }

  public void updateProximity(double prox){
	  this.prox = prox;

  }
  
  public void setMetrics(double cpu, double mem, double bw, long storage,
		  String[] appIDs, double minRatio){
    this.cpu = cpu;
    this.mem = mem;
    this.bw = bw;
    this.storage = storage;
    this.appIDs = appIDs == null? null : Arrays.asList(appIDs);
    this.minRatio = minRatio;
    this.updateRate();
  }
  
  public void setSeqNumber(long seqNumber){
      this.seqNumber = seqNumber;
  }
  
  public void setAvailability(boolean isAvailable) {
	  this.isAvailable = isAvailable;
  }
  
  public boolean isAvailable() {
	  return this.isAvailable;
  }
  
  public double getCPU() {
	  return cpu;
  }
  public double getMem() {
	  return mem;
  }
  public double getBW() {
	  return bw;
  }
  public long getStorage() {
	  return storage;
  }
  public boolean containsApp(String appID) {
	  if(appIDs == null) return false;
	  return appIDs.contains(appID);
  }
  public double getMinRatio() {
	  return minRatio;
  }
  
  public String toString() {
    return "Node " + node.getId() + " CPU: " + cpu + ", MEM: " +
    	mem + ", BW: " + bw + ", storage: " + storage + ", minRatio: " + minRatio + 
    	", Rate="+this.rate;
  }

}
